#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__all__ = ['Custom', 'DEFAULT_CUSTOM']

from pyLot.core import Datum, Container

class Custom(object):
  """

  Custom is a special type of style-sheet.
  A Style is generally used to customize widget aspect and is applied 
  recursively to all children
  
  A Custom is used to customize widget and layout creation and is applied 
  recursively according to criteria.

  - chooser      : chooser class to use (ITypedChooser)
  - filter       : filter class to use (ITypedFilter)
  - selector     : selector class to use (ITypedSelector)
  - func_check   : tests if value is valid (check function)
  - func_match   : tests if value match filter criterion (match function)
  - ignore_datum : ignore_datum ? (bool)
  - enabled      : is enabled ? (bool)
  - editable     : is editable ? (bool)
  - shared_list  : list used by widget (list, in genral: list of unicode)
  - orientation  : layout orientation (identical to Qt orientation)
    * Qt.Horizontal
    * Qt.Vertical
  - label        : display labels

  """

  def __init__(self):

    self.all_dict = {}
    self.callers = {}
    self._keys = [
      u'chooser',
      u'filter',
      u'selector',
      u'editable',
      u'enabled',
      u'func_check',
      u'func_match',
      u'ignore_datum',
      u'label',
      u'orientation',
      u'reverse_label_data',
      u'shared_list',
      u'style',
      ]


    self._fields = [u'type', u'group', u'nature', u'id', None]

    for key in self._keys :
      self.all_dict[key] = {}
      self.callers[key] = []
      for field in self._fields :
        self.all_dict[key][field] = {}

  def set(self, key, value, field_id=None, field_value=None, caller=None):
    """    
    field_id:
      For a given "arg" object (argument passed by caller) :
      
      - type : isinstance(arg, field_value)
      - group : arg.group == field_value
      - nature : arg.nature is field_value
      - id : id(arg) == id(field_value)
    
    Example 1 :
    To use non editable predefined comboboxes for all unicode-type datum objects,
    you can use this customization :
    custom.set(u'chooser', GuiPredefUnicodeChooser, u'nature', unicode)

    Example 2 :
    To disable all data for a given datum group :
    custom.set(u'enabled', False, u'group', u'read_only')

    Example 3 :
    Use vertical orientation only for filters
    custom.set(u'orientation', Qt.Vertical, caller=GuiFilterToolBox)

    Use vertical orientation for all objects
    custom.set(u'orientation', Qt.Vertical)
    """
    if field_id == 'id' :

      if id(field_value) in self.all_dict[key][field_id] :
        if caller :
          self.all_dict[key][field_id][id(field_value)][0].append(caller)
      else :
        if caller :
          self.all_dict[key][field_id][id(field_value)] = ([caller], value)
        else :
          self.all_dict[key][field_id][id(field_value)] = ([], value)

    else :

      if field_value in self.all_dict[key][field_id] :
        if caller :
          self.all_dict[key][field_id][field_value][0].append(caller)
      else :
        if caller :
          self.all_dict[key][field_id][field_value] = ([caller], value)
        else :
          self.all_dict[key][field_id][field_value] = ([], value)

  def getOption(self, key, caller, data, default):
    return self._get(key, caller, data, default)

  def getCallerOption(self, key, caller, data, default):
    raise NotImplementedError

  def _get(self, key, caller, data, default):
    _type = self.all_dict[key][u'type']
    _nature = self.all_dict[key][u'nature']
    _group = self.all_dict[key][u'group']
    _id = self.all_dict[key][u'id']

    if isinstance(data, (Datum, Container)):
      data_group = data.group
      data_nature = data.nature

    else :
      data_group = None
      data_nature = None

    data_id = id(data)
    types = tuple([dtype for caller, dtype in _type.values()])

    if data_id in _id :
      opt = _id[data_id]
    elif data_group in _group :
      opt = _group[data_group]
    elif data_nature in _nature :
      opt = _nature[data_nature]
    elif isinstance(data, types):
      for t in _type :
        if isinstance(data, t):
          break
      opt = _type[t]
    elif None in self.all_dict[key]:
      try:
        opt = self.all_dict[key][None][None]
      except KeyError :
        opt = ([], default)
    else :
      opt = ([], default)

    excpected_callers, value = opt

    if caller is None :
      # caller is not explicitly defined, for example, a function or an user
      # call Custom().getOption
      return value
    elif not excpected_callers :
      # No restrictions on caller types
      return value
    elif isinstance(caller, tuple(excpected_callers)):
      # There are restrictions on callers and current caller matches
      return value
    else :
      # caller do not match, return default
      return default


DEFAULT_CUSTOM = Custom()


if __name__ == u'__main__' :
  pass
